package com.me.study.javaCore.ssl;

import com.me.common.util.StringUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.*;
import java.security.KeyStore;

/**
 * Java SSL 认证：客户端使用 TrustManager（信任的公钥库）去验证服务端。    <P></P>
 *
 * 证书加载过程：KeyManagerFactory、TrustManagerFactory => KeyManager、TrustManager => SSLContext => SSLEngine、SSLSocketFactory、SSLSocket
 *
 * @author ME
 * @date 2021/8/9
 */
public class SSLClient {

    private static final String DEFAULT_HOST = "127.0.0.1";
    private static final int DEFAULT_PORT = 7777;
    private static final String CLIENT_KEY_STORE_PASSWORD = "123456";
    private static final String CLIENT_TRUST_KEY_STORE_PASSWORD = "123456";
    private SSLSocket client;

    /**
     * 启动客户端程序
     *
     * @param args
     */
    public static void main(String[] args) {
        SSLClient client = new SSLClient();
        client.init();
        client.process();
    }

    /**
     * 通过ssl socket与服务端进行连接,并且发送一个消息
     */
    public void process() {
        if (client == null) {
            System.out.println("ERROR");
            return;
        }
        try {
            DataOutputStream output = new DataOutputStream(new BufferedOutputStream(client.getOutputStream()));
            output.writeUTF("Client Message：one way...");
            output.writeUTF(SSLServer.END);
            output.flush();
            client.shutdownOutput();

            DataInputStream input = new DataInputStream(new BufferedInputStream(client.getInputStream()));
            // 循环读取
            StringBuilder msg = new StringBuilder();
            String temp;
            int index;
            while (StringUtils.isNotEmpty(temp = input.readUTF())) {
                // 遇到 eof 时就结束接收
                if ((index = temp.indexOf(SSLServer.EOF)) != -1) {
                    msg.append(temp, 0, index);
                    break;
                }
                msg.append(temp);
            }
            System.out.println("【server】 " + msg);

            client.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * ssl连接的重点：
     * 初始化 SSLSocket
     * 导入客户端私钥 KeyStore，导入客户端受信任的 KeyStore（服务端的证书）
     */
    public void init() {
        try {
            KeyStore tks = KeyStore.getInstance("JKS");
            tks.load(new FileInputStream("testJDK8/src/main/java/com/me/study/javaCore/ssl/tclient.jks"),
                    CLIENT_TRUST_KEY_STORE_PASSWORD.toCharArray());
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(tks);

            SSLContext ctx = SSLContext.getInstance("SSL");
            ctx.init(null, tmf.getTrustManagers(), null);
            // 打印这个SSLContext实例使用的协议
            System.out.println("缺省安全套接字使用的协议: " + ctx.getProtocol());

            SSLSocketFactory socketFactory = ctx.getSocketFactory();
            client = (SSLSocket) socketFactory.createSocket(DEFAULT_HOST, DEFAULT_PORT);

            // 设置可用的加密套件
            // String[] enabledCiphers = {"ECDHE-ECDSA-AES128-GCM-SHA256"};
            // client.setEnabledCipherSuites(enabledCiphers);

            // 强制要求客户端认证。如果客户端未提供有效的证书，SSL/TLS 握手将失败，连接将被拒绝。
            client.setNeedClientAuth(true);
            // 希望客户端进行认证，但不强制要求。
            // sslSocket.setWantClientAuth(true);
        } catch (Exception e) {
            System.out.println(e);
        }
    }

}
